{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "6ec1671c",
   "metadata": {},
   "source": [
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain-academy/blob/main/module-4/sub-graph.ipynb) [![Open in LangChain Academy](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66e9eba12c7b7688aa3dbb5e_LCA-badge-green.svg)](https://academy.langchain.com/courses/take/intro-to-langgraph/lessons/58239937-lesson-2-sub-graphs)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "3db85080-2299-4885-a2f6-fffa6a09a238",
   "metadata": {},
   "source": [
    "# Sub-graphs\n",
    "\n",
    "## Review\n",
    "\n",
    "We're building up to a multi-agent research assistant that ties together all of the modules from this course.\n",
    "\n",
    "We just covered parallelization, which is one important LangGraph controllability topic.\n",
    "\n",
    "## Goals\n",
    "\n",
    "Now, we're [going to cover sub-graphs](https://langchain-ai.github.io/langgraph/how-tos/subgraph/#simple-example).\n",
    "\n",
    "## State\n",
    "\n",
    "Sub-graphs allow you to create and manage different states in different parts of your graph. \n",
    "\n",
    "This is particularly useful for multi-agent systems, with teams of agents that each have their own state.\n",
    "\n",
    "Let's consider a toy example:\n",
    "\n",
    "* I have a system that accepts logs\n",
    "* It performs two separate sub-tasks by different agents (summarize logs, find failure modes)\n",
    "* I want to perform these two operations in two different sub-graphs.\n",
    "\n",
    "The most critical thing to understand is how the graphs communicate! \n",
    "\n",
    "In short, communication is **done with over-lapping keys**: \n",
    "\n",
    "* The sub-graphs can access `docs` from the parent\n",
    "* The parent can access `summary/failure_report` from the sub-graphs\n",
    "\n",
    "![subgraph.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbb1abf89f2d847ee6f1ff_sub-graph1.png)\n",
    "\n",
    "## Input\n",
    "\n",
    "Let's define a schema for the logs that will be input to our graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2954e8c6-496f-4394-b56a-681608bf65da",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install -U  langgraph"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e7e413ba-e376-4a5f-a666-2d2154aa6fe2",
   "metadata": {},
   "source": [
    "We'll use [LangSmith](https://docs.smith.langchain.com/) for [tracing](https://docs.smith.langchain.com/concepts/tracing)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "05b26c51",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os, getpass\n",
    "\n",
    "def _set_env(var: str):\n",
    "    if not os.environ.get(var):\n",
    "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
    "\n",
    "_set_env(\"LANGCHAIN_API_KEY\")\n",
    "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
    "os.environ[\"LANGCHAIN_PROJECT\"] = \"langchain-academy\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "3efaf8bb-f675-4c0b-a575-89c7e2987a33",
   "metadata": {},
   "outputs": [],
   "source": [
    "from operator import add\n",
    "from typing_extensions import TypedDict\n",
    "from typing import List, Optional, Annotated\n",
    "\n",
    "# The structure of the logs\n",
    "class Log(TypedDict):\n",
    "    id: str\n",
    "    question: str\n",
    "    docs: Optional[List]\n",
    "    answer: str\n",
    "    grade: Optional[int]\n",
    "    grader: Optional[str]\n",
    "    feedback: Optional[str]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15825627-78c2-4ba0-ad11-95e4afdb771d",
   "metadata": {},
   "source": [
    "## Sub graphs\n",
    "\n",
    "Here is the failure analysis sub-graph, which uses `FailureAnalysisState`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f32986a9-6d11-4646-b2c0-fbae4f524579",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.display import Image, display\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "\n",
    "# Failure Analysis Sub-graph\n",
    "class FailureAnalysisState(TypedDict):\n",
    "    cleaned_logs: List[Log]\n",
    "    failures: List[Log]\n",
    "    fa_summary: str\n",
    "    processed_logs: List[str]\n",
    "\n",
    "class FailureAnalysisOutputState(TypedDict):\n",
    "    fa_summary: str\n",
    "    processed_logs: List[str]\n",
    "\n",
    "def get_failures(state):\n",
    "    \"\"\" Get logs that contain a failure \"\"\"\n",
    "    cleaned_logs = state[\"cleaned_logs\"]\n",
    "    failures = [log for log in cleaned_logs if \"grade\" in log]\n",
    "    return {\"failures\": failures}\n",
    "\n",
    "def generate_summary(state):\n",
    "    \"\"\" Generate summary of failures \"\"\"\n",
    "    failures = state[\"failures\"]\n",
    "    # Add fxn: fa_summary = summarize(failures)\n",
    "    fa_summary = \"Poor quality retrieval of Chroma documentation.\"\n",
    "    return {\"fa_summary\": fa_summary, \"processed_logs\": [f\"failure-analysis-on-log-{failure['id']}\" for failure in failures]}\n",
    "\n",
    "fa_builder = StateGraph(input=FailureAnalysisState,output=FailureAnalysisOutputState)\n",
    "fa_builder.add_node(\"get_failures\", get_failures)\n",
    "fa_builder.add_node(\"generate_summary\", generate_summary)\n",
    "fa_builder.add_edge(START, \"get_failures\")\n",
    "fa_builder.add_edge(\"get_failures\", \"generate_summary\")\n",
    "fa_builder.add_edge(\"generate_summary\", END)\n",
    "\n",
    "graph = fa_builder.compile()\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa83f44c-0bb9-48c6-afec-dad536e608fa",
   "metadata": {},
   "source": [
    "Here is the question summarization sub-grap, which uses `QuestionSummarizationState`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "7149000c-ffb6-4834-bd9e-d35b36c524e7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Summarization subgraph\n",
    "class QuestionSummarizationState(TypedDict):\n",
    "    cleaned_logs: List[Log]\n",
    "    qs_summary: str\n",
    "    report: str\n",
    "    processed_logs: List[str]\n",
    "\n",
    "class QuestionSummarizationOutputState(TypedDict):\n",
    "    report: str\n",
    "    processed_logs: List[str]\n",
    "\n",
    "def generate_summary(state):\n",
    "    cleaned_logs = state[\"cleaned_logs\"]\n",
    "    # Add fxn: summary = summarize(generate_summary)\n",
    "    summary = \"Questions focused on usage of ChatOllama and Chroma vector store.\"\n",
    "    return {\"qs_summary\": summary, \"processed_logs\": [f\"summary-on-log-{log['id']}\" for log in cleaned_logs]}\n",
    "\n",
    "def send_to_slack(state):\n",
    "    qs_summary = state[\"qs_summary\"]\n",
    "    # Add fxn: report = report_generation(qs_summary)\n",
    "    report = \"foo bar baz\"\n",
    "    return {\"report\": report}\n",
    "\n",
    "qs_builder = StateGraph(input=QuestionSummarizationState,output=QuestionSummarizationOutputState)\n",
    "qs_builder.add_node(\"generate_summary\", generate_summary)\n",
    "qs_builder.add_node(\"send_to_slack\", send_to_slack)\n",
    "qs_builder.add_edge(START, \"generate_summary\")\n",
    "qs_builder.add_edge(\"generate_summary\", \"send_to_slack\")\n",
    "qs_builder.add_edge(\"send_to_slack\", END)\n",
    "\n",
    "graph = qs_builder.compile()\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f10a5baf-beab-4927-807a-3e6a5ad3d202",
   "metadata": {},
   "source": [
    "## Adding sub graphs to our parent graph\n",
    "\n",
    "Now, we can bring it all together.\n",
    "\n",
    "We create our parent graph with `EntryGraphState`. \n",
    "\n",
    "And we add our sub-graphs as nodes! \n",
    "\n",
    "```\n",
    "entry_builder.add_node(\"question_summarization\", qs_builder.compile())\n",
    "entry_builder.add_node(\"failure_analysis\", fa_builder.compile())\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "587c8fe1-1ae8-411e-a55d-cac299026646",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Entry Graph\n",
    "class EntryGraphState(TypedDict):\n",
    "    raw_logs: List[Log]\n",
    "    cleaned_logs: Annotated[List[Log], add] # This will be USED BY in BOTH sub-graphs\n",
    "    fa_summary: str # This will only be generated in the FA sub-graph\n",
    "    report: str # This will only be generated in the QS sub-graph\n",
    "    processed_logs:  Annotated[List[int], add] # This will be generated in BOTH sub-graphs"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d4da397-310c-4453-969a-e0ae2cc75db8",
   "metadata": {},
   "source": [
    "But, why does `cleaned_logs` have a reducer if it only goes *into* each sub-graph as an input? It is not modified.\n",
    "\n",
    "```\n",
    "cleaned_logs: Annotated[List[Log], add] # This will be USED BY in BOTH sub-graphs\n",
    "```\n",
    "\n",
    "This is because the output state of the subgraphs will contain **all keys**, even if they are unmodified. \n",
    "\n",
    "The sub-graphs are run in parallel.\n",
    "\n",
    "Because the parallel sub-graphs return the same key, it needs to have a reducer like `operator.add` to combine the incoming values from each sub-graph.\n",
    "\n",
    "But, we can work around this by using another concept we talked about before.\n",
    "\n",
    "We can simply create an output state schema for each sub-graph and ensure that the output state schema contains different keys to publish as output.\n",
    "\n",
    "We don't actually need each sub-graph to output `cleaned_logs`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "50092b9b-70c1-41b1-a74a-254683e28ce0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Entry Graph\n",
    "class EntryGraphState(TypedDict):\n",
    "    raw_logs: List[Log]\n",
    "    cleaned_logs: List[Log]\n",
    "    fa_summary: str # This will only be generated in the FA sub-graph\n",
    "    report: str # This will only be generated in the QS sub-graph\n",
    "    processed_logs:  Annotated[List[int], add] # This will be generated in BOTH sub-graphs\n",
    "\n",
    "def clean_logs(state):\n",
    "    # Get logs\n",
    "    raw_logs = state[\"raw_logs\"]\n",
    "    # Data cleaning raw_logs -> docs \n",
    "    cleaned_logs = raw_logs\n",
    "    return {\"cleaned_logs\": cleaned_logs}\n",
    "\n",
    "entry_builder = StateGraph(EntryGraphState)\n",
    "entry_builder.add_node(\"clean_logs\", clean_logs)\n",
    "entry_builder.add_node(\"question_summarization\", qs_builder.compile())\n",
    "entry_builder.add_node(\"failure_analysis\", fa_builder.compile())\n",
    "\n",
    "entry_builder.add_edge(START, \"clean_logs\")\n",
    "entry_builder.add_edge(\"clean_logs\", \"failure_analysis\")\n",
    "entry_builder.add_edge(\"clean_logs\", \"question_summarization\")\n",
    "entry_builder.add_edge(\"failure_analysis\", END)\n",
    "entry_builder.add_edge(\"question_summarization\", END)\n",
    "\n",
    "graph = entry_builder.compile()\n",
    "\n",
    "from IPython.display import Image, display\n",
    "\n",
    "# Setting xray to 1 will show the internal structure of the nested graph\n",
    "display(Image(graph.get_graph(xray=1).draw_mermaid_png()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "17af1254-4e75-4349-9a79-295f4ec95016",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'raw_logs': [{'id': '1',\n",
       "   'question': 'How can I import ChatOllama?',\n",
       "   'answer': \"To import ChatOllama, use: 'from langchain_community.chat_models import ChatOllama.'\"}],\n",
       " 'cleaned_logs': [{'id': '1',\n",
       "   'question': 'How can I import ChatOllama?',\n",
       "   'answer': \"To import ChatOllama, use: 'from langchain_community.chat_models import ChatOllama.'\"}],\n",
       " 'fa_summary': 'Poor quality retrieval of Chroma documentation.',\n",
       " 'report': 'foo bar baz',\n",
       " 'processed_logs': ['summary-on-log-1']}"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Dummy logs\n",
    "question_answer = Log(\n",
    "    id=\"1\",\n",
    "    question=\"How can I import ChatOllama?\",\n",
    "    answer=\"To import ChatOllama, use: 'from langchain_community.chat_models import ChatOllama.'\",\n",
    ")\n",
    "\n",
    "question_answer_feedback = Log(\n",
    "    id=\"2\",\n",
    "    question=\"How can I use Chroma vector store?\",\n",
    "    answer=\"To use Chroma, define: rag_chain = create_retrieval_chain(retriever, question_answer_chain).\",\n",
    "    grade=0,\n",
    "    grader=\"Document Relevance Recall\",\n",
    "    feedback=\"The retrieved documents discuss vector stores in general, but not Chroma specifically\",\n",
    ")\n",
    "\n",
    "raw_logs = [question_answer,question_answer_feedback]\n",
    "graph.invoke({\"raw_logs\": raw_logs})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9192d228-4d3d-4fb0-8bea-26772c3d2e0b",
   "metadata": {},
   "source": [
    "## LangSmith\n",
    "\n",
    "Let's look at the LangSmith trace:\n",
    "\n",
    "https://smith.langchain.com/public/f8f86f61-1b30-48cf-b055-3734dfceadf2/r"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
